﻿using System;
using System.Collections.Generic;
using System.Linq;
using HK.Core.Common;
using HK.Core.Common.Data;
using HK.Editor.GUIEx.Protocol.TreeView.Data;

namespace HK.Editor.GUIEx.TreeView.Data
{
    /// <summary>
    /// 树视图模型参数异常
    /// </summary>
    public class TreeModelArgumentNullException : ArgumentNullException
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iMessage">Message</param>
        public TreeModelArgumentNullException(string iMessage) : base(iMessage) {}
        
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iParam">参数名</param>
        /// <param name="iMessage">Message</param>
        public TreeModelArgumentNullException(string iParam, string iMessage) : base(iParam, iMessage) {}
    }
    
    /// <summary>
    /// 表述数据容器
    /// </summary>
    /// <typeparam name="TBranch">树枝类型</typeparam>
    public class TableTreeDataContainer<TBranch> : ClassExtension, ITableTreeDataContainer<TBranch>
        where TBranch : JsonData, ITreeBranch
    {
        
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iBranches">树枝列表</param>
        public TableTreeDataContainer (IList<TBranch> iBranches)
        {
            SetBranches(iBranches);
        }
        
        private TBranch _root = default(TBranch);
        /// <summary>
        /// 根节点
        /// </summary>
        public TBranch Root
        {
            get => _root;
            private set => _root = value;
        }

        private IList<TBranch> _branches = null;
        /// <summary>
        /// 树枝列表
        /// </summary>
        public IList<TBranch> Branches
        {
            get => _branches;
            private set => _branches = value;
        }

        /// <summary>
        /// 最大Id
        /// </summary>
        public int MaxId { get; set; } = 0;

#region Event
        
        private ModelChanged _onChanged = () =>
        {
#if UNITY_EDITOR
            // var descTmp = string.IsNullOrEmpty(desc) ? "-" : desc;
            // Loger.Info($"AbManager::OnStateChanged():{state}({target.Type}) - {target.Key}({descTmp})");
#endif
        };
        
        /// <summary>
        /// 变更事件
        /// </summary>
        public event ModelChanged Changed
        {

            add
            {
#if UNITY_EDITOR
                if (null != value)
                {
                    Info("Changed(): + {0}:{1}", value.Target.GetType().FullName, value.Method.Name);
                }
#endif
                _onChanged += value;
            }
            remove
            {

#if UNITY_EDITOR
                if (null != value)
                {
                    Info("Changed(): - {0}:{1}", value.Target.GetType().FullName, value.Method.Name);
                }
#endif
                if (null != _onChanged) _onChanged -= value;
            }
        }

#endregion
        
        /// <summary>
        /// 设置树枝列表
        /// </summary>
        /// <param name="iBranches">树枝列表</param>
        public void SetBranches(IList<TBranch> iBranches)
        {
            Init (iBranches);
        }
        
        public TBranch Find (int iId)
        {
            if (null == _branches || 0 >= _branches.Count) return default(TBranch);
            
            return _branches.FirstOrDefault (branch => branch.Id == iId);
        }
        
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="iBranches">树枝列表</param>
        /// <exception cref="ArgumentNullException"></exception>
        private void Init (IList<TBranch> iBranches)
        {
            if (null == iBranches || 0 >= iBranches.Count)
            {
                throw new TreeModelArgumentNullException("iBranches", "Input data is null. Ensure input is a non-null list.");
            }
            
            Branches = iBranches;
            if (0 < iBranches.Count)
            {
                Root = TreeBranchUtility.ListToTreeBranch(iBranches);
                // 最大Id
                MaxId = iBranches.Max(iE => iE.Id);
            }
            else
            {
                MaxId = 0;
            }
        }
        
        /// <summary>
        /// 生成唯一树枝Id
        /// </summary>
        /// <returns>唯一树枝Id</returns>
        public int GenerateBranchID()
        {
            return ++MaxId;
        }

        /// <summary>
        /// 生成祖先Id列表
        /// </summary>
        /// <param name="iId">Id</param>
        /// <returns>祖先Id列表</returns>
        public IList<int> GetAncestorIds(int iId)
        {
            var parents = new List<int>();
            ITreeBranch target = Find(iId);
            if (null != target)
            {
                while (null != target.Parent)
                {
                    parents.Add(target.Parent.Id);
                    target = target.Parent;
                }
            }
            return parents;
        }
        
        public IList<int> GetDescendantsThatHaveChildren (int iId)
        {
            var searchFromThis = Find(iId);
            if (searchFromThis != null)
            {
                return GetParentsBelowStackBased(searchFromThis);
            }
            return new List<int>();
        }
        
        IList<int> GetParentsBelowStackBased(TBranch searchFromThis)
        {
            var stack = new Stack<ITreeBranch>();
            stack.Push(searchFromThis);

            var parentsBelow = new List<int>();
            while (stack.Count > 0)
            {
                var current = stack.Pop();
                if (current.HasChildren)
                {
                    parentsBelow.Add(current.Id);
                    foreach (var branch in current.Children)
                    {
                        stack.Push(branch);
                    }
                }
            }

            return parentsBelow;
        }
        
        public void RemoveBranches (IList<int> iBranchIdes)
        {
            IList<TBranch> branches = _branches.Where (element => iBranchIdes.Contains (element.Id)).ToArray ();
            if(0 >= branches.Count) return;
            RemoveBranches (branches);
        }

        public void RemoveBranches (IList<TBranch> iBranches)
        {
            foreach (var branch in iBranches)
            {
                if (branch == _root)
                {
                    throw new TreeModelArgumentNullException("It is not allowed to remove the root element");
                }
            }
		
            var ancestors = TreeBranchUtility.FindCommonAncestorsWithinList (iBranches);
            foreach (var ancestor in ancestors)
            {
                ancestor.Parent.Children.Remove (ancestor);
                ancestor.Parent = null;
            }

            TreeBranchUtility.TreeBranchToList(_root, _branches);

            _onChanged?.Invoke();
        }
        
        
        public void AddBranches (IList<TBranch> iBranches, TBranch iParent, int iInsertPosition)
        {
            if (iBranches == null)
            {
                throw new TreeModelArgumentNullException("iBranches", "elements is null");
            }

            if (0 >= iBranches.Count)
            {
                throw new TreeModelArgumentNullException("iBranches", "elements Count is 0: nothing to add");
            }

            if (null == iParent)
            {
                throw new TreeModelArgumentNullException("iParent", "parent is null");
            }

            if (!iParent.ValidChildren)
            {
                iParent.Children = new List<ITreeBranch>();
            }

            iParent.Children.InsertRange(iInsertPosition, iBranches.Cast<TBranch> ());
            foreach (var branch in iBranches)
            {
                branch.Parent = iParent;
                branch.Depth = iParent.Depth + 1;
                TreeBranchUtility.UpdateDepthValues(branch);
            }

            TreeBranchUtility.TreeBranchToList(_root, _branches);
            
            _onChanged?.Invoke();
            
        }
        
        public void AddBranch (TBranch iBranch, ITreeBranch iParent, int insertPosition)
        {
            if (null == iBranch)
            {
                throw new TreeModelArgumentNullException("iBranch", "branch is null");
            }
            
            if (null == iParent)
            {
                throw new TreeModelArgumentNullException("iParent", "parent is null");
            }

            if (!iParent.ValidChildren)
            {
                iParent.Children = new List<ITreeBranch> ();
            }

            iParent.Children.Insert (insertPosition, iBranch);
            iBranch.Parent = iParent;

            TreeBranchUtility.UpdateDepthValues(iParent);
            TreeBranchUtility.TreeBranchToList(_root, _branches);
            
            _onChanged?.Invoke();
        }
        
        public void MoveBranches(ITreeBranch iParent, int iInsertIndex, List<ITreeBranch> iBranches)
        {
            if (0 > iInsertIndex)
            {
                throw new TreeModelArgumentNullException("Invalid input: insertionIndex is -1, client needs to decide what index elements should be reparented at");
            }
                
            if (null == iParent) return;

            // We are moving items so we adjust the insertion index to accomodate that any items above the insertion index is removed before inserting
            if (iInsertIndex > 0)
            {
                iInsertIndex -= iParent.Children.GetRange(0, iInsertIndex).Count(iBranches.Contains);
            }

            // Remove draggedItems from their parents
            foreach (var branch in iBranches)
            {
                branch.Parent.Children.Remove(branch);         // remove from old parent
                branch.Parent = iParent;					   // set new parent
            }

            if (!iParent.ValidChildren)
            {
                iParent.Children = new List<ITreeBranch>();
            }

            // Insert dragged items under new parent
            iParent.Children.InsertRange(iInsertIndex, iBranches);

            TreeBranchUtility.UpdateDepthValues (iParent);
            TreeBranchUtility.TreeBranchToList (_root, _branches);
            
            _onChanged?.Invoke();
        }
        
        public void AddRoot (TBranch iRoot)
        {
            if (null == iRoot)
            {
                throw new TreeModelArgumentNullException("iRoot", "root is null");
            }

            if (null == _branches)
            {
                throw new TreeModelArgumentNullException("Internal Error: data list is null");
            }

            if (0 != _branches.Count)
            {
                throw new TreeModelArgumentNullException("AddRoot is only allowed on empty data list");
            }

            iRoot.Id = GenerateBranchID();
            iRoot.Depth = -1;
            _branches.Add (iRoot);
        }
    }
}
